package cf;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import lucene.PaperSearcher;
import model.RelevantNode;

import org.apache.mahout.cf.taste.impl.model.file.FileDataModel;
import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;
import org.apache.mahout.cf.taste.impl.similarity.TanimotoCoefficientSimilarity;
import org.apache.mahout.cf.taste.impl.similarity.UncenteredCosineSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.similarity.ItemSimilarity;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;

import com.mysql.jdbc.Connection;
import com.mysql.jdbc.ResultSet;
import com.mysql.jdbc.Statement;

import data.TestUserSelection;


public class ItemCF {
	private DataModel model;
	private UserSimilarity userSimilarity;
	private ItemSimilarity itemSimilarity;
	private int topN = 50;
	private UserNeighborhood userNeighborhood;
	//private String user;
	private Connection conn;
	private Statement stmt = null;
	private ResultSet rs = null;
	private PaperSearcher searcher;
	//private HashMap<String, Long> authorMap = new HashMap<String, Long>();
	private HashMap<String, Double> authorSimMap = new HashMap<String,Double>();

	public ItemCF() throws Exception {
		init();
	}

	private void init() throws Exception {
		Class.forName("com.mysql.jdbc.Driver").newInstance();
		String url = "jdbc:mysql://localhost:3306/" + "bibsonomy";
		String dbuser = "root";
		String dbpass = "123456";
		Connection con = (Connection) DriverManager.getConnection(url, dbuser,
				dbpass);
		this.conn = con;
		this.stmt = (Statement) conn.createStatement();
		initModel();
		
		File indexDir = new File("D:\\CityU\\project\\bibsonomy\\"
				+ "\\BibIndex\\");
		this.searcher = new PaperSearcher(indexDir);	
		
	}

	private HashSet<Long> initHoldoutPapers(String user) throws Exception {
		String path = "D:\\CityU\\project\\bibsonomy\\experiment\\" + user
				+ "\\test.txt";
		BufferedReader reader = new BufferedReader(new FileReader(path));
		HashSet<Long> holdoutPapers = new HashSet<Long>();
		String line = null;
		while ((line = reader.readLine()) != null) {
			line = line.trim();
			holdoutPapers.add(Long.valueOf(line));
		}
		reader.close();
		return holdoutPapers;
	}

	private void initModel() throws Exception {
		//String path = "D:\\CityU\\project\\bibsonomy\\data\\" + "tags.txt";
		String path = "D:\\CityU\\project\\bibsonomy\\data\\" + "user_author.txt";
		this.model = new FileDataModel(new File(path));
		//this.similarity = new TanimotoCoefficientSimilarity(model);
		this.userSimilarity = new UncenteredCosineSimilarity(model);
		this.itemSimilarity = new TanimotoCoefficientSimilarity(model);
		this.userNeighborhood = new NearestNUserNeighborhood(topN, userSimilarity,
				model);
	}

	public void predict(String user) throws Exception {
		HashSet<Long> holdoutPapers = initHoldoutPapers(user);
		String path = "D:\\CityU\\project\\bibsonomy\\experiment\\" + user
				+ "\\result\\cf\\";
		File dir = new File(path);
		if (!dir.exists()) {
			dir.mkdirs();
		}

		//long[] userNeighbors = userNeighborhood.getUserNeighborhood(Long.valueOf(user));
		
		HashSet<String> trainPapers = userTrainPapers(user);
		System.out.println("run test user\t" + user + "\ttrain size\t" + trainPapers.size() );
		
		HashMap<String, HashSet<String>> tagMap = new HashMap<String, HashSet<String>>();
		
		for (long testCase : holdoutPapers) {
			HashMap<String, Double> map = new HashMap<String, Double>();
			HashSet<Long> authors = extractAuthor(testCase + "");
									
			for (String trainPaper : trainPapers) {
				HashSet<Long> trainPaperAuthors = extractAuthor(trainPaper);

				HashSet<String> tags = tagMap.get(user + "\t" + trainPaper);
				if(tags == null) {
					tags = retrieveTags(user, trainPaper);
					tagMap.put(user+ "\t" + trainPaper, tags);
				}
				
				for (long au : authors) {

					for (long tau : trainPaperAuthors) {
                        Double value = 0.0;
                        Double obj = authorSimMap.get(tau + "\t" + au); 
                        if( obj == null) {
                        	value = itemSimilarity.itemSimilarity(tau, au);
                        	if (value.isNaN())
								value = 0.0;
							authorSimMap.put(tau + "\t" + au, value);
							authorSimMap.put(au + "\t" + tau, value);
                        } else{
                        	value = obj;
                        }
																							
						for (String tag : tags) {							
							String key = tag;
							double v = 0.0;
							if (map.get(key) == null) {
								v = value;
								map.put(key, v);
								continue;
							}
							v = map.get(key);
							v = v+ value;
							map.put(key, v);
						}
					}
				}
			}
			
			List<RelevantNode> results = new ArrayList<RelevantNode>();
			for(Map.Entry<String,Double> entry : map.entrySet()) {
				String t = entry.getKey();
				double score = entry.getValue();
				RelevantNode rn = new RelevantNode(t,score);
				results.add(rn);
			}
			Collections.sort(results);
			String file = path + testCase + ".txt";
			writeResults(results, file);
		}
        tagMap = null;
        //System.gc();
	}
	
	private HashSet<String> retrieveTags(String user, String item)
			throws Exception {
		rs = (ResultSet) stmt
				.executeQuery("select tag from tas_new where user = '" + user
						+ "' and content_id = '" + item + "'");
		HashSet<String> tags = new HashSet<String>();
		while (rs.next()) {
			String tag = rs.getString("tag");
			tag = tag.trim();
			tags.add(tag);
		}
		return tags;
	}
	
	private HashSet<String> userTrainPapers(String user) throws Exception{
		String path = "D:\\CityU\\project\\bibsonomy\\experiment\\" + user
				+ "\\train.txt";
		BufferedReader reader = new BufferedReader(new FileReader(path));
		HashSet<String> set = new HashSet<String>();
		String line = null;
		while ((line = reader.readLine()) != null) {
			line = line.trim();
			set.add(line);
		}
		return set;
	}
	
	private void writeResults(List<RelevantNode> results, String file) throws Exception{
		BufferedWriter writer = new BufferedWriter(new FileWriter(file)); 
		try{
			for(RelevantNode rn : results) {
				String t = rn.getPid();
				double s = rn.getScore();
				writer.write(t + "\t" + s);
				writer.newLine();				
			}
			
		} finally{
			writer.flush();
			writer.close();
		}
		
	}
	
	private HashSet<Long> extractAuthor(String item) throws Exception {
		HashSet<Long> authors = new HashSet<Long>();
		String au = searcher.retrieveByPid(item).get("author");
		
		StringTokenizer tokens = new StringTokenizer(au);

		//String[] tokens = au.split(" and ");
		while (tokens.hasMoreTokens()) {
			String str = tokens.nextToken();
			str = str.trim();			
			authors.add(Long.valueOf(str));
		}

		return authors;
	}
	
	private void clear() throws Exception{
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException sqlEx) {
			} // ignore
			rs = null;
		}

		if (stmt != null) {
			try {
				stmt.close();
			} catch (SQLException sqlEx) {
			} // ignore
			stmt = null;
		}
		conn.close();
	}
	
	public static HashSet<String> doneCases() throws Exception{
		String path = "D:\\CityU\\project\\bibsonomy\\doneCases.txt";
		HashSet<String> cases = new HashSet<String>();
		BufferedReader reader = new BufferedReader(new FileReader(path));
		String line = null;
		while((line = reader.readLine()) != null) {
			line = line.trim();
			String[] tokens = line.split("\t");
			String doneCase = tokens[1];
			cases.add(doneCase);
		}
		return cases;
	}
	
	public static void main(String[] args) throws Exception{
		List<String> users = TestUserSelection.retrieveTestUsers();
		//HashSet<String> doneCases = doneCases();
		ItemCF app = new ItemCF();
		for (String user : users) {			
			//if(doneCases.contains(user))
			//	continue;
			app.predict(user);			
		}
		app.clear();
	}
	
	
}
